//----------------------------------------------------------------------------
//  ,-.
// /_\___ ,   Ninja Framework
// '  ___\    (C) Riccardo Capra - http://riccar.do
//  //   \\
//----------------------------------------------------------------------------
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------------

#include "NjExample01.h"
#include "NjSharedSyncObjectTest.h"
#include "NjSynchronizationTest.h"
#include "NjSharedSyncObjectConstants.h"
#include "NjHighResolutionTimer.h"
#include "NjPlatformSyncObject.h"
#include "NjThreadsSystem.h"
#include "NjPlacementNew.h"
#include "NjTestUnit.h"
#include "NjRandUint.h"
#include "NjExampleRandGenerator.h"
#include "NjSynchronizationStats.h"
#include "NjMemUtils.h"

template <typename tSubSyncObject>
class NjHelper
{
public:
	typedef tSubSyncObject Type;

	NJ_DISABLE_INSTANCE(NjHelper);
};

template <>
class NjHelper<NjRWLock::Read>
{
public:
	typedef NjRWLock::Write Type;

	NJ_DISABLE_INSTANCE(NjHelper);
};

template <typename tSyncObject, typename tSubSyncObject>
class NjSharedSyncObjectTest<tSyncObject, tSubSyncObject>::Static
{
public:
	typedef NjSharedSyncObjectConstants::Query<tSyncObject, tSubSyncObject> Constants;
	typedef tSyncObject SyncObject;
	typedef tSubSyncObject SubSyncObject;
	typedef typename NjHelper<tSubSyncObject>::Type SubSyncObjectTryLockFail;

	static void RunPerformancesLock();
	static void InitializeSyncObject(SyncObject & kSubSyncObject);
	static SubSyncObject & GetSubSyncObject(SyncObject & kSyncObject);
	static SubSyncObjectTryLockFail & GetSubSyncObjectTryLockFail(SyncObject & kSyncObject);

	enum ErrorState
	{
		STATUS_SUCCESS,
		NOT_INITIALIZED
	};

	class SharedData01;

	class ThreadData01
	{
	public:
		NJ_TRIVIAL_DEFAULT_CONSTRUCTOR(ThreadData01);
		NJ_TRIVIAL_DESTRUCTOR(ThreadData01);

		static const NjUint ITERATIONS = 100;

		SharedData01 * m_pSharedData;
		NjThreadsSystem::Thread * m_pThread;
		ErrorState m_eErrorState;

		static void ThreadFunct(void * pData);

		NJ_DISABLE_COPY(ThreadData01);
	};

	class SharedData01
	{
	public:
		NJ_TRIVIAL_DEFAULT_CONSTRUCTOR(SharedData01);
		NJ_TRIVIAL_DESTRUCTOR(SharedData01);

		static const NjUint TOT_THREADS = 20;
		static const NjUint TOT_SYNC_OBJECTS = 5;
		static const NjUint TOT_ENTRIES = ThreadData01::ITERATIONS*TOT_THREADS*4;

		SyncObject m_aSyncObjects[TOT_SYNC_OBJECTS];
		ThreadData01 m_aThreadsData[TOT_THREADS];

		enum LockState
		{
			TRY_LOCK_START,
			LOCK_START,
			LOCK_END,
			LOCK_FAIL,
			UNLOCK_START,
			UNLOCK_END
		};

		class Entry
		{
		public:
			NJ_TRIVIAL_DEFAULT_CONSTRUCTOR(Entry);
			NJ_TRIVIAL_DESTRUCTOR(Entry);

			NjUint m_uSyncObject;
			LockState m_eLockState;
#if NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
			NjUint8 m_aSnapshot[sizeof(SyncObject)];
#endif // NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
			ThreadData01 * m_pThreadData;

			NJ_DISABLE_COPY(Entry);
		};

		Entry m_aEntries[TOT_ENTRIES];
		NjAtomic<NjUint> m_kTotEntries;

		NJ_DISABLE_COPY(SharedData01);
	};

	class SharedData02
	{
	public:
		NJ_TRIVIAL_DEFAULT_CONSTRUCTOR(SharedData02);
		NJ_TRIVIAL_DESTRUCTOR(SharedData02);

		NjSemaphore m_kWaitLock;
		NjSemaphore m_kStartUnlock;
		NjPlatformSyncObject::SyncObject * m_pPlatformSync;
		SyncObject * m_pImplSync;
		NjUint m_uTotal;

		static void ThreadFunct(void * pData);
	};

	NJ_DISABLE_INSTANCE(Static);
};
//----------------------------------------------------------------------------
template <typename tSyncObject, typename tSubSyncObject>
void NjSharedSyncObjectTest<tSyncObject, tSubSyncObject>::RunFunctional()
{
	NJ_TEST_UNITS_GROUP(Static::Constants::ms_szSyncObject);
		NJ_TEST_UNIT("Constructors, Destructors, Lock, TryLock, Unlock");
			typename Static::SyncObject kSyncObject;
			Static::InitializeSyncObject(kSyncObject);
			typename Static::SubSyncObject & kSubSyncObject(Static::GetSubSyncObject(kSyncObject));

			NJ_TEST(kSubSyncObject.TryLock(NjEventsSystem::NO_WAIT));
			kSubSyncObject.Unlock();

			NJ_TEST(kSubSyncObject.TryLock(NjEventsSystem::NO_WAIT));
			kSubSyncObject.Unlock();

			kSubSyncObject.Lock();
			kSubSyncObject.Unlock();

			NJ_TEST(kSubSyncObject.TryLock(NjEventsSystem::NO_WAIT));
			kSubSyncObject.Unlock();

			NjBool bRecursive(Static::Constants::ms_eType==NjSharedSyncObjectConstants::TYPE_RECURSIVE ? NJ_TRUE : NJ_FALSE);
			if (bRecursive)
			{
				kSubSyncObject.Lock();
				NJ_TEST(kSubSyncObject.TryLock(NjEventsSystem::NO_WAIT));
				kSubSyncObject.Unlock();
				kSubSyncObject.Unlock();
			}
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
}
//----------------------------------------------------------------------------
template <>
void NjSharedSyncObjectTest<NjPulseEvent>::RunFunctional()
{
}
//----------------------------------------------------------------------------
template <typename tSyncObject, typename tSubSyncObject>
void NjSharedSyncObjectTest<tSyncObject, tSubSyncObject>::RunFunctionalMultithreaded()
{
	NJ_TEST_UNITS_GROUP(Static::Constants::ms_szSyncObject);
		NJ_TEST_UNIT("Constructors, Destructors, Lock, TryLock, Unlock (multi threaded)");
			typename Static::SharedData01 * const pSharedData01(NjNew(typename Static::SharedData01));

			typename Static::SyncObject * pSyncObjects(pSharedData01->m_aSyncObjects);
			typename Static::SyncObject const * const pSyncObjectsEnd(pSyncObjects+Static::SharedData01::TOT_SYNC_OBJECTS);
			while(pSyncObjects<pSyncObjectsEnd)
			{
				Static::InitializeSyncObject(*pSyncObjects);
				++pSyncObjects;
			}

			pSharedData01->m_kTotEntries.Set(0);

			typename Static::ThreadData01 * pPos(pSharedData01->m_aThreadsData);
			typename Static::ThreadData01 * const pEnd(pPos+Static::SharedData01::TOT_THREADS);

			while (pPos!=pEnd)
			{
				pPos->m_pSharedData=pSharedData01;
				pPos->m_pThread=NjThreadsSystem::CreateThread(Static::ThreadData01::ThreadFunct, pPos);
				pPos->m_eErrorState=Static::NOT_INITIALIZED;

				NJ_TEST(pPos->m_pThread!=NJ_NULL);
				++pPos;
			}

			pPos=pSharedData01->m_aThreadsData;

			while (pPos!=pEnd)
			{
				NJ_TEST(NjThreadsSystem::TryJoinThread(pPos->m_pThread, (pEnd+1-pPos)*Static::ThreadData01::ITERATIONS*NjSharedSyncObjectConstants::TIME_GRANULARITY)==NjEventsSystem::TRY_SUCCESS);
				NJ_TEST(NjThreadsSystem::DestroyThread(pPos->m_pThread));
				NJ_TEST(pPos->m_eErrorState==Static::STATUS_SUCCESS);
				++pPos;
			}

			typename Static::SharedData01::Entry const * pEntry(pSharedData01->m_aEntries);
			typename Static::SharedData01::Entry const * const pEntryEnd(pEntry+pSharedData01->m_kTotEntries.Get());

			typename Static::SyncObject * aOwnedSyncObject[Static::SharedData01::TOT_THREADS];
			NjMemUtils::Zero(aOwnedSyncObject, sizeof(aOwnedSyncObject));
			NjUint aOwnedSyncObjectCounter[Static::SharedData01::TOT_THREADS];
			NjMemUtils::Zero(aOwnedSyncObjectCounter, sizeof(aOwnedSyncObjectCounter));

			typename Static::ThreadData01 * aSyncObjectOwner[Static::SharedData01::TOT_SYNC_OBJECTS];
			NjMemUtils::Zero(aSyncObjectOwner, sizeof(aSyncObjectOwner));
			NjBool bRecursive(Static::Constants::ms_eType==NjSharedSyncObjectConstants::TYPE_RECURSIVE ? NJ_TRUE : NJ_FALSE);

			while (pEntry<pEntryEnd)
			{
				NjUint const uThread(pEntry->m_pThreadData-pSharedData01->m_aThreadsData);
				NJ_TEST(uThread<Static::SharedData01::TOT_THREADS);

				NjUint const uSyncObject(pEntry->m_uSyncObject);
				NJ_TEST(uSyncObject<Static::SharedData01::TOT_SYNC_OBJECTS);

				typename Static::SyncObject * const pSyncObject(pSharedData01->m_aSyncObjects+uSyncObject);
				typename Static::ThreadData01 * const pThreadsData(pEntry->m_pThreadData);

				switch(pEntry->m_eLockState)
				{
					case Static::SharedData01::TRY_LOCK_START:
					case Static::SharedData01::LOCK_START:
						NJ_TEST((aOwnedSyncObject[uThread]==NJ_NULL && aOwnedSyncObjectCounter[uThread]==0) ||
								(aOwnedSyncObject[uThread]==pSyncObject && aOwnedSyncObjectCounter[uThread]>0 && bRecursive));
						aOwnedSyncObject[uThread]=pSyncObject;
						++aOwnedSyncObjectCounter[uThread];
						break;

					case Static::SharedData01::LOCK_END:
						NJ_TEST(aOwnedSyncObject[uThread]==pSyncObject);
						NJ_TEST(aOwnedSyncObjectCounter[uThread]>0);
						NJ_TEST((aSyncObjectOwner[uSyncObject]==NJ_NULL && aOwnedSyncObjectCounter[uThread]==1) ||
								(aSyncObjectOwner[uSyncObject]==pThreadsData && aOwnedSyncObjectCounter[uThread]>1 && bRecursive));
						aSyncObjectOwner[uSyncObject]=pThreadsData;
						break;

					case Static::SharedData01::LOCK_FAIL:
						NJ_TEST(aOwnedSyncObject[uThread]==pSyncObject);
						NJ_TEST(aOwnedSyncObjectCounter[uThread]==1);
						NJ_TEST(aSyncObjectOwner[uSyncObject]!=pThreadsData);
						aOwnedSyncObject[uThread]=NJ_NULL;
						--aOwnedSyncObjectCounter[uThread];
						break;

					case Static::SharedData01::UNLOCK_START:
						NJ_TEST(aOwnedSyncObject[uThread]==pSyncObject);
						NJ_TEST(aOwnedSyncObjectCounter[uThread]>0);
						NJ_TEST(aSyncObjectOwner[uSyncObject]==pThreadsData);

						if (aOwnedSyncObjectCounter[uThread]==1)
							aSyncObjectOwner[uSyncObject]=NJ_NULL;
						else
						{
							NJ_TEST(bRecursive);
						}
						break;

					case Static::SharedData01::UNLOCK_END:
						NJ_TEST(aOwnedSyncObject[uThread]==pSyncObject);
						NJ_TEST(aOwnedSyncObjectCounter[uThread]>0);

						--aOwnedSyncObjectCounter[uThread];
						if (aOwnedSyncObjectCounter[uThread]==0)
							aOwnedSyncObject[uThread]=NJ_NULL;
						else
						{
							NJ_TEST(bRecursive);
						}
						break;

					default:
						NJ_TEST_FAIL();
				}
				++pEntry;
			}

			NjDelete(pSharedData01);
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
}
//----------------------------------------------------------------------------
template <>
void NjSharedSyncObjectTest<NjPulseEvent>::RunFunctionalMultithreaded()
{
}
//----------------------------------------------------------------------------
template <typename tSyncObject, typename tSubSyncObject>
void NjSharedSyncObjectTest<tSyncObject, tSubSyncObject>::RunPerformances()
{
	RunPerformancesConstructor();
	Static::RunPerformancesLock();
}
//----------------------------------------------------------------------------
template <>
void NjSharedSyncObjectTest<NjPulseEvent>::RunPerformances()
{
}
//----------------------------------------------------------------------------
template <typename tSyncObject, typename tSubSyncObject>
void NjSharedSyncObjectTest<tSyncObject, tSubSyncObject>::RunLocker()
{
	NJ_TEST_UNITS_GROUP(Static::Constants::ms_szSyncObject);
		NJ_TEST_UNIT("Locker - Constructors, Destructors, Unlock");
			typename Static::SyncObject kSyncObject;
			Static::InitializeSyncObject(kSyncObject);
			typename Static::SubSyncObject & kSubSyncObject(Static::GetSubSyncObject(kSyncObject));

			{
				typename Static::SubSyncObject::Locker kNullLocker(NJ_NULL);
			}

			NJ_TEST(kSubSyncObject.TryLock(0));
			kSubSyncObject.Unlock();

			{
				typename Static::SubSyncObject::Locker kNullLocker(NJ_NULL);
				kNullLocker.Unlock();
			}

			NJ_TEST(kSubSyncObject.TryLock(0));
			kSubSyncObject.Unlock();
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
}
//----------------------------------------------------------------------------
template <>
void NjSharedSyncObjectTest<NjPulseEvent>::RunLocker()
{
}
//----------------------------------------------------------------------------
template <typename tSyncObject, typename tSubSyncObject>
void NjSharedSyncObjectTest<tSyncObject, tSubSyncObject>::RunPerformancesConstructor()
{
	NJ_TEST_UNITS_GROUP(Static::Constants::ms_szSyncObject);
		NJ_TEST_UNIT("Constructors, Destructors (performances)");
			typedef typename Static::SyncObject SyncObject;

			static const NjUint OPERATIONS = 40000000;
			static const NjUint ITERATIONS = 2000;

			NjUint * const pOperations(NjNewArray(NjUint[ITERATIONS]));

			NjUint uTotOperations(OPERATIONS);
			NjRandUint<EXAMPLE_RAND_GENERATOR> kRand(0, 21);
			NjUint uIteration(ITERATIONS);
			NjUint uMaxOperations(0);
			while (uIteration>0)
			{
				NjUint uOperations(uTotOperations/uIteration);
				--uIteration;

				if (uIteration>0)
					uOperations+=(NjInt(uOperations)*NjInt(kRand()-10))/100;

				if (uOperations>uMaxOperations)
					uMaxOperations=uOperations;

				pOperations[uIteration]=uOperations;
				uTotOperations-=uOperations;
			}

			NjUint8 * const pBuffer(NjNew(NjUint8[(sizeof(SyncObject)+sizeof(NjPlatformSyncObject::SyncObject))*uMaxOperations]));
			SyncObject * const pSyncObjects(reinterpret_cast<SyncObject *>(pBuffer));
			NjPlatformSyncObject::SyncObject * const pPlatformSyncObjects(reinterpret_cast<NjPlatformSyncObject::SyncObject *>(pSyncObjects+uMaxOperations));

			NjHighResolutionTimer kPlatformSyncObjectCreate;
			NjHighResolutionTimer kPlatformSyncObjectDestroy;
			NjHighResolutionTimer kSyncObjectCreate;
			NjHighResolutionTimer kSyncObjectDestroy;
			NjHighResolutionTimer kOverHead;

			NjUint const * pPos(pOperations);
			NjUint const * const pEnd(pOperations+ITERATIONS);

			while (pPos!=pEnd)
			{
				NjUint const uOperations(*pPos);
				SyncObject * pImplPos(pSyncObjects);
				SyncObject const * const pImplEnd(pSyncObjects+uOperations);
				NjPlatformSyncObject::SyncObject * pPlatformPos(pPlatformSyncObjects);
				NjPlatformSyncObject::SyncObject const * const pPlatformEnd(pPlatformPos+uOperations);

				kOverHead.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					++pPlatformPos;
				}
				kOverHead.Pause();

				pPlatformPos=pPlatformSyncObjects;
				kPlatformSyncObjectCreate.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Initialize(*pPlatformPos);
					++pPlatformPos;
				}
				kPlatformSyncObjectCreate.Pause();

				pPlatformPos=pPlatformSyncObjects;
				kPlatformSyncObjectDestroy.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Destroy(*pPlatformPos);
					++pPlatformPos;
				}
				kPlatformSyncObjectDestroy.Pause();

				kSyncObjectCreate.Start();
				while (pImplPos<pImplEnd)
				{
					NjPlacementNew kPlacement(pImplPos);
					NJ_PLACEMENT_NEW(kPlacement, SyncObject);
					++pImplPos;
				}
				kSyncObjectCreate.Pause();

				pImplPos=pSyncObjects;
				kSyncObjectDestroy.Start();
				while (pImplPos<pImplEnd)
				{
					pImplPos->~SyncObject();
					++pImplPos;
				}
				kSyncObjectDestroy.Pause();

				++pPos;
			}

			NjDeleteArray(pBuffer);
			NjDeleteArray(pOperations);

			NjFloat fOverHead(kOverHead.GetTimeS());
			NjFloat fPlatformSyncObjectCreate(kPlatformSyncObjectCreate.GetTimeS()-fOverHead);
			NjFloat fPlatformSyncObjectDestroy(kPlatformSyncObjectDestroy.GetTimeS()-fOverHead);
			NjFloat fSyncObjectCreate(kSyncObjectCreate.GetTimeS()-fOverHead);
			NjFloat fSyncObjectDestroy(kSyncObjectDestroy.GetTimeS()-fOverHead);

			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::CREATION, OPERATIONS, fPlatformSyncObjectCreate);
			NjSynchronizationStats::Store(Static::Constants::ms_eCreationRow, NjSynchronizationStats::CREATION, OPERATIONS, fSyncObjectCreate);
			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::DESCTURCTION, OPERATIONS, fPlatformSyncObjectDestroy);
			NjSynchronizationStats::Store(Static::Constants::ms_eCreationRow, NjSynchronizationStats::DESCTURCTION, OPERATIONS, fSyncObjectDestroy);
			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::CLASS_SIZE, 1, sizeof(NjPlatformSyncObject::SyncObject));
			NjSynchronizationStats::Store(Static::Constants::ms_eCreationRow, NjSynchronizationStats::CLASS_SIZE, 1, sizeof(SyncObject));
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
}
//----------------------------------------------------------------------------
template <typename tSyncObject, typename tSubSyncObject>
void NjSharedSyncObjectTest<tSyncObject, tSubSyncObject>::Static::RunPerformancesLock()
{
	NJ_TEST_UNITS_GROUP(Static::Constants::ms_szSyncObject);
		NJ_TEST_UNIT("Lock, TryLock, Unlock (performances)");
			static const NjUint OPERATIONS = 40000000;
			static const NjUint ITERATIONS = 2000;
			static NjBool s_bResult=NJ_FALSE;

			NjUint * const pOperations(NjNewArray(NjUint[ITERATIONS]));

			NjUint uTotOperations(OPERATIONS);
			NjRandUint<EXAMPLE_RAND_GENERATOR> kRand(0, 21);
			NjUint uIteration(ITERATIONS);
			NjUint uMaxOperations(0);
			while (uIteration>0)
			{
				NjUint uOperations(uTotOperations/uIteration);
				--uIteration;

				if (uIteration>0)
					uOperations+=(NjInt(uOperations)*NjInt(kRand()-10))/100;

				if (uOperations>uMaxOperations)
					uMaxOperations=uOperations;

				pOperations[uIteration]=uOperations;
				uTotOperations-=uOperations;
			}

			NjUint8 * const pBuffer(NjNew(NjUint8[(sizeof(SyncObject)+sizeof(NjPlatformSyncObject::SyncObject))*uMaxOperations*2]));
			Static::SyncObject * const pSyncObjects(reinterpret_cast<SyncObject *>(pBuffer));
			NjPlatformSyncObject::SyncObject * const pPlatformSyncObjects(reinterpret_cast<NjPlatformSyncObject::SyncObject *>(pSyncObjects+uMaxOperations*2));

			SharedData02 kSharedData;
			kSharedData.m_pPlatformSync=pPlatformSyncObjects+uMaxOperations;
			kSharedData.m_pImplSync=pSyncObjects+uMaxOperations;
			kSharedData.m_uTotal=uMaxOperations;

			{
				NjPlatformSyncObject::SyncObject * pPlatformPos(pPlatformSyncObjects);
				NjPlatformSyncObject::SyncObject const * const pPlatformEnd(pPlatformPos+uMaxOperations*2);
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Initialize(*pPlatformPos);
					++pPlatformPos;
				}

				SyncObject * pImplPos(pSyncObjects);
				SyncObject const * const pImplEnd(pImplPos+uMaxOperations*2);
				while (pImplPos<pImplEnd)
				{
					NjPlacementNew kPlacement(pImplPos);
					Static::InitializeSyncObject(*NJ_PLACEMENT_NEW(kPlacement, SyncObject));

					++pImplPos;
				}
			}

			NjThreadsSystem::Thread * const pThread(NjThreadsSystem::CreateThread(SharedData02::ThreadFunct, &kSharedData));
			NJ_TEST(pThread!=NJ_NULL);
			kSharedData.m_kWaitLock.Lock();

			NjHighResolutionTimer kPlatformSyncObjectLock;
			NjHighResolutionTimer kPlatformSyncObjectUnlock;
			NjHighResolutionTimer kPlatformSyncObjectTryLockSuccess;
			NjHighResolutionTimer kPlatformSyncObjectTryLockFail;
			NjHighResolutionTimer kSyncObjectLock;
			NjHighResolutionTimer kSyncObjectUnlock;
			NjHighResolutionTimer kSyncObjectTryLockSuccess;
			NjHighResolutionTimer kSyncObjectTryLockFail;
			NjHighResolutionTimer kOverHead;

			NjUint const * pPos(pOperations);
			NjUint const * const pEnd(pOperations+ITERATIONS);

			while (pPos!=pEnd)
			{
				NjUint const uOperations(*pPos);
				Static::SyncObject * pImplPos(pSyncObjects);
				Static::SyncObject const * const pImplEnd(pSyncObjects+uOperations);
				Static::SyncObject * pImplTryFailPos(pSyncObjects+uMaxOperations);
				Static::SyncObject const * const pImplTryFailEnd(pImplTryFailPos+uOperations);
				NjPlatformSyncObject::SyncObject * pPlatformPos(pPlatformSyncObjects);
				NjPlatformSyncObject::SyncObject const * const pPlatformEnd(pPlatformPos+uOperations);
				NjPlatformSyncObject::SyncObject * pPlatformTryFailPos(pPlatformPos+uMaxOperations);
				NjPlatformSyncObject::SyncObject const * const pPlatformTryFailEnd(pPlatformTryFailPos+uOperations);

				kOverHead.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					++pPlatformPos;
				}
				kOverHead.Pause();

				kPlatformSyncObjectTryLockFail.Start();
				while (pPlatformTryFailPos<pPlatformTryFailEnd)
				{
					s_bResult=NjPlatformSyncObject::TryLock(*pPlatformTryFailPos, 0);
					++pPlatformTryFailPos;
				}
				kPlatformSyncObjectTryLockFail.Pause();

				pPlatformPos=pPlatformSyncObjects;
				kPlatformSyncObjectLock.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Lock(*pPlatformPos);
					++pPlatformPos;
				}
				kPlatformSyncObjectLock.Pause();

				pPlatformPos=pPlatformSyncObjects;
				kPlatformSyncObjectUnlock.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Unlock(*pPlatformPos);
					++pPlatformPos;
				}
				kPlatformSyncObjectUnlock.Pause();

				pPlatformPos=pPlatformSyncObjects;
				kPlatformSyncObjectTryLockSuccess.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					s_bResult=NjPlatformSyncObject::TryLock(*pPlatformPos, 0);
					++pPlatformPos;
				}
				kPlatformSyncObjectTryLockSuccess.Pause();

				pPlatformPos=pPlatformSyncObjects;
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Unlock(*pPlatformPos);
					++pPlatformPos;
				}

				kSyncObjectTryLockFail.Start();
				while (pImplTryFailPos<pImplTryFailEnd)
				{
					s_bResult=Static::GetSubSyncObject(*pImplTryFailPos).TryLock(0);
					++pImplTryFailPos;
				}
				kSyncObjectTryLockFail.Pause();

				kSyncObjectLock.Start();
				while (pImplPos<pImplEnd)
				{
					Static::GetSubSyncObject(*pImplPos).Lock();
					++pImplPos;
				}
				kSyncObjectLock.Pause();

				pImplPos=pSyncObjects;
				kSyncObjectUnlock.Start();
				while (pImplPos<pImplEnd)
				{
					Static::GetSubSyncObject(*pImplPos).Unlock();
					++pImplPos;
				}
				kSyncObjectUnlock.Pause();

				pImplPos=pSyncObjects;
				kSyncObjectTryLockSuccess.Start();
				while (pImplPos<pImplEnd)
				{
					s_bResult=Static::GetSubSyncObject(*pImplPos).TryLock(0);
					++pImplPos;
				}
				kSyncObjectTryLockSuccess.Pause();

				pImplPos=pSyncObjects;
				while (pImplPos<pImplEnd)
				{
					Static::GetSubSyncObject(*pImplPos).Unlock();
					++pImplPos;
				}
				++pPos;
			}

			kSharedData.m_kStartUnlock.Unlock();
			NJ_TEST(NjThreadsSystem::JoinThread(pThread));
			NJ_TEST(NjThreadsSystem::DestroyThread(pThread));

			{
				NjPlatformSyncObject::SyncObject * pPlatformPos(pPlatformSyncObjects);
				NjPlatformSyncObject::SyncObject const * const pPlatformEnd(pPlatformPos+uMaxOperations*2);
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Destroy(*pPlatformPos);
					++pPlatformPos;
				}

				Static::SyncObject * pImplPos(pSyncObjects);
				Static::SyncObject const * const pImplEnd(pSyncObjects+uMaxOperations*2);
				while (pImplPos<pImplEnd)
				{
					pImplPos->~SyncObject();
					++pImplPos;
				}
			}

			NjDeleteArray(pBuffer);
			NjDeleteArray(pOperations);

			NjFloat fOverHead(kOverHead.GetTimeS());
			NjFloat fPlatformSyncObjectLock(kPlatformSyncObjectLock.GetTimeS()-fOverHead);
			NjFloat fPlatformSyncObjectUnlock(kPlatformSyncObjectUnlock.GetTimeS()-fOverHead);
			NjFloat fPlatformSyncObjectTryLockSuccess(kPlatformSyncObjectTryLockSuccess.GetTimeS()-fOverHead);
			NjFloat fPlatformSyncObjectTryLockFail(kPlatformSyncObjectTryLockFail.GetTimeS()-fOverHead);
			NjFloat fSyncObjectLock(kSyncObjectLock.GetTimeS()-fOverHead);
			NjFloat fSyncObjectUnlock(kSyncObjectUnlock.GetTimeS()-fOverHead);
			NjFloat fSyncObjectTryLockSuccess(kSyncObjectTryLockSuccess.GetTimeS()-fOverHead);
			NjFloat fSyncObjectTryLockFail(kSyncObjectTryLockFail.GetTimeS()-fOverHead);

			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::METH_LOCK, OPERATIONS, fPlatformSyncObjectLock);
			NjSynchronizationStats::Store(Static::Constants::ms_eLockRow, NjSynchronizationStats::METH_LOCK, OPERATIONS, fSyncObjectLock);
			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::METH_UNLOCK, OPERATIONS, fPlatformSyncObjectUnlock);
			NjSynchronizationStats::Store(Static::Constants::ms_eLockRow, NjSynchronizationStats::METH_UNLOCK, OPERATIONS, fSyncObjectUnlock);
			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::METH_TRY_LOCK_SUCCESS, OPERATIONS, fPlatformSyncObjectTryLockSuccess);
			NjSynchronizationStats::Store(Static::Constants::ms_eLockRow, NjSynchronizationStats::METH_TRY_LOCK_SUCCESS, OPERATIONS, fSyncObjectTryLockSuccess);
			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::METH_TRY_LOCK_FAIL, OPERATIONS, fPlatformSyncObjectTryLockFail);
			NjSynchronizationStats::Store(Static::Constants::ms_eLockRow, NjSynchronizationStats::METH_TRY_LOCK_FAIL, OPERATIONS, fSyncObjectTryLockFail);

		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
}
//----------------------------------------------------------------------------
template <>
void NjSharedSyncObjectTest<NjPulseEvent>::Static::RunPerformancesLock()
{
}
//----------------------------------------------------------------------------
template <>
inline void NjSharedSyncObjectTest<NjDummySyncObject>::Static::InitializeSyncObject(NjDummySyncObject & kSyncObject)
{
	NJ_UNUSED_VAR(kSyncObject);
}
//----------------------------------------------------------------------------
template <>
inline void NjSharedSyncObjectTest<NjMutex>::Static::InitializeSyncObject(NjMutex & kSyncObject)
{
	NJ_UNUSED_VAR(kSyncObject);
}
//----------------------------------------------------------------------------
template <>
inline void NjSharedSyncObjectTest<NjRecursiveMutex>::Static::InitializeSyncObject(NjRecursiveMutex & kSyncObject)
{
	NJ_UNUSED_VAR(kSyncObject);
}
//----------------------------------------------------------------------------
template <>
inline void NjSharedSyncObjectTest<NjSemaphore>::Static::InitializeSyncObject(NjSemaphore & kSyncObject)
{
	kSyncObject.Unlock();
}
//----------------------------------------------------------------------------
template <>
inline void NjSharedSyncObjectTest<NjRWLock, NjRWLock::Read>::Static::InitializeSyncObject(NjRWLock & kSyncObject)
{
	NJ_UNUSED_VAR(kSyncObject);
}
//----------------------------------------------------------------------------
template <>
inline void NjSharedSyncObjectTest<NjRWLock, NjRWLock::Write>::Static::InitializeSyncObject(NjRWLock & kSyncObject)
{
	NJ_UNUSED_VAR(kSyncObject);
}
//----------------------------------------------------------------------------
template <>
inline void NjSharedSyncObjectTest<NjPulseEvent>::Static::InitializeSyncObject(NjPulseEvent & kSyncObject)
{
	NJ_UNUSED_VAR(kSyncObject);
}
//----------------------------------------------------------------------------
template <>
inline NjDummySyncObject & NjSharedSyncObjectTest<NjDummySyncObject>::Static::GetSubSyncObject(NjDummySyncObject & kSyncObject)
{
	return kSyncObject;
}
//----------------------------------------------------------------------------
template <>
inline NjMutex & NjSharedSyncObjectTest<NjMutex>::Static::GetSubSyncObject(NjMutex & kSyncObject)
{
	return kSyncObject;
}
//----------------------------------------------------------------------------
template <>
inline NjRecursiveMutex & NjSharedSyncObjectTest<NjRecursiveMutex>::Static::GetSubSyncObject(NjRecursiveMutex & kSyncObject)
{
	return kSyncObject;
}
//----------------------------------------------------------------------------
template <>
inline NjSemaphore & NjSharedSyncObjectTest<NjSemaphore>::Static::GetSubSyncObject(NjSemaphore & kSyncObject)
{
	return kSyncObject;
}
//----------------------------------------------------------------------------
template <>
inline NjRWLock::Read & NjSharedSyncObjectTest<NjRWLock, NjRWLock::Read>::Static::GetSubSyncObject(NjRWLock & kSyncObject)
{
	return kSyncObject.GetRead();
}
//----------------------------------------------------------------------------
template <>
inline NjRWLock::Write & NjSharedSyncObjectTest<NjRWLock, NjRWLock::Write>::Static::GetSubSyncObject(NjRWLock & kSyncObject)
{
	return kSyncObject.GetWrite();
}
//----------------------------------------------------------------------------
template <>
inline NjPulseEvent & NjSharedSyncObjectTest<NjPulseEvent>::Static::GetSubSyncObject(NjPulseEvent & kSyncObject)
{
	return kSyncObject;
}
//----------------------------------------------------------------------------
template <>
inline NjDummySyncObject & NjSharedSyncObjectTest<NjDummySyncObject>::Static::GetSubSyncObjectTryLockFail(NjDummySyncObject & kSyncObject)
{
	return kSyncObject;
}
//----------------------------------------------------------------------------
template <>
inline NjMutex & NjSharedSyncObjectTest<NjMutex>::Static::GetSubSyncObjectTryLockFail(NjMutex & kSyncObject)
{
	return kSyncObject;
}
//----------------------------------------------------------------------------
template <>
inline NjRecursiveMutex & NjSharedSyncObjectTest<NjRecursiveMutex>::Static::GetSubSyncObjectTryLockFail(NjRecursiveMutex & kSyncObject)
{
	return kSyncObject;
}
//----------------------------------------------------------------------------
template <>
inline NjSemaphore & NjSharedSyncObjectTest<NjSemaphore>::Static::GetSubSyncObjectTryLockFail(NjSemaphore & kSyncObject)
{
	return kSyncObject;
}
//----------------------------------------------------------------------------
template <>
inline NjRWLock::Write & NjSharedSyncObjectTest<NjRWLock, NjRWLock::Read>::Static::GetSubSyncObjectTryLockFail(NjRWLock & kSyncObject)
{
	return kSyncObject.GetWrite();
}
//----------------------------------------------------------------------------
template <>
inline NjRWLock::Write & NjSharedSyncObjectTest<NjRWLock, NjRWLock::Write>::Static::GetSubSyncObjectTryLockFail(NjRWLock & kSyncObject)
{
	return kSyncObject.GetWrite();
}
//----------------------------------------------------------------------------
template <>
inline NjPulseEvent & NjSharedSyncObjectTest<NjPulseEvent>::Static::GetSubSyncObjectTryLockFail(NjPulseEvent & kSyncObject)
{
	return kSyncObject;
}
//----------------------------------------------------------------------------
template <typename tSyncObject, typename tSubSyncObject>
inline void NjSharedSyncObjectTest<tSyncObject, tSubSyncObject>::Static::ThreadData01::ThreadFunct(void * pData)
{
	ThreadData01 & kThreadData(*reinterpret_cast<ThreadData01 *>(pData));
	SharedData01 & kSharedData(*kThreadData.m_pSharedData);
	NjRandUint<EXAMPLE_RAND_GENERATOR> kRand(0, SharedData01::TOT_SYNC_OBJECTS);

	NjUint uIterations(ITERATIONS);
	NjBool bRecursive(Static::Constants::ms_eType==NjSharedSyncObjectConstants::TYPE_RECURSIVE ? NJ_TRUE : NJ_FALSE);
	while (uIterations>0)
	{
		--uIterations;

		NjUint const uSyncObject(kRand());
		SyncObject * pSyncObject(kSharedData.m_aSyncObjects+uSyncObject);
		SubSyncObject & kSubSyncObject(GetSubSyncObject(*pSyncObject));

		NjUint uLockCount(0);

		for(;;)
		{
			NjBool const bTryLock(kRand()%2==0);

			NjUint uIndex(kSharedData.m_kTotEntries.PostIncrement());
			typename SharedData01::Entry * pEntry(kSharedData.m_aEntries+uIndex);
			pEntry->m_uSyncObject=uSyncObject;
			pEntry->m_pThreadData=&kThreadData;
#if NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
			NjMemUtils::Copy(pEntry->m_aSnapshot, sizeof(pEntry->m_aSnapshot), pSyncObject, sizeof(SyncObject));
#endif // NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT

			if (bTryLock)
			{
				pEntry->m_eLockState=SharedData01::TRY_LOCK_START;
				if (!kSubSyncObject.TryLock(NjSharedSyncObjectConstants::TIME_GRANULARITY))
				{
					uIndex=kSharedData.m_kTotEntries.PostIncrement();
					pEntry=kSharedData.m_aEntries+uIndex;
					pEntry->m_uSyncObject=uSyncObject;
					pEntry->m_pThreadData=&kThreadData;
					pEntry->m_eLockState=SharedData01::LOCK_FAIL;
#if NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
					NjMemUtils::Copy(pEntry->m_aSnapshot, sizeof(pEntry->m_aSnapshot), pSyncObject, sizeof(SyncObject));
#endif // NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
					break;
				}
			}
			else
			{
				pEntry->m_eLockState=SharedData01::LOCK_START;
				kSubSyncObject.Lock();
			}

			uIndex=kSharedData.m_kTotEntries.PostIncrement();
			pEntry=kSharedData.m_aEntries+uIndex;
			pEntry->m_uSyncObject=uSyncObject;
			pEntry->m_pThreadData=&kThreadData;
			pEntry->m_eLockState=SharedData01::LOCK_END;
#if NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
			NjMemUtils::Copy(pEntry->m_aSnapshot, sizeof(pEntry->m_aSnapshot), pSyncObject, sizeof(SyncObject));
#endif // NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT

			NjThreadsSystem::Sleep(NjSharedSyncObjectConstants::TIME_GRANULARITY);
			++uLockCount;

			if (uIterations<=0 || (!bRecursive) || (kRand()%8)!=0)
				break;

			--uIterations;
		}

		while (uLockCount>0)
		{
			--uLockCount;

			NjUint uIndex(kSharedData.m_kTotEntries.PostIncrement());
			typename SharedData01::Entry * pEntry(kSharedData.m_aEntries+uIndex);
			pEntry->m_uSyncObject=uSyncObject;
			pEntry->m_pThreadData=&kThreadData;
			pEntry->m_eLockState=SharedData01::UNLOCK_START;
#if NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
			NjMemUtils::Copy(pEntry->m_aSnapshot, sizeof(pEntry->m_aSnapshot), pSyncObject, sizeof(SyncObject));
#endif // NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
			kSubSyncObject.Unlock();

			uIndex=kSharedData.m_kTotEntries.PostIncrement();
			pEntry=kSharedData.m_aEntries+uIndex;
			pEntry->m_uSyncObject=uSyncObject;
			pEntry->m_pThreadData=&kThreadData;
			pEntry->m_eLockState=SharedData01::UNLOCK_END;
#if NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
			NjMemUtils::Copy(pEntry->m_aSnapshot, sizeof(pEntry->m_aSnapshot), pSyncObject, sizeof(SyncObject));
#endif // NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
		}
	}

	kThreadData.m_eErrorState=STATUS_SUCCESS;
}
//----------------------------------------------------------------------------
template <>
inline void NjSharedSyncObjectTest<NjPulseEvent>::Static::ThreadData01::ThreadFunct(void * pData)
{
	NJ_UNUSED_VAR(pData);
}
//----------------------------------------------------------------------------
template <typename tSyncObject, typename tSubSyncObject>
void NjSharedSyncObjectTest<tSyncObject, tSubSyncObject>::Static::SharedData02::ThreadFunct(void * pData)
{
	SharedData02 * const pSharedData(reinterpret_cast<SharedData02 *>(pData));
	if (pSharedData==NJ_NULL)
		return;

	NjPlatformSyncObject::SyncObject * pPlatformPos(pSharedData->m_pPlatformSync);
	NjPlatformSyncObject::SyncObject const * const pPlatformEnd(pPlatformPos+pSharedData->m_uTotal);
	while (pPlatformPos<pPlatformEnd)
	{
		NjPlatformSyncObject::Lock(*pPlatformPos);
		++pPlatformPos;
	}

	SyncObject * pImplPos(pSharedData->m_pImplSync);
	SyncObject const * const pImplEnd(pImplPos+pSharedData->m_uTotal);
	while (pImplPos<pImplEnd)
	{
		Static::GetSubSyncObjectTryLockFail(*pImplPos).Lock();
		++pImplPos;
	}

	pSharedData->m_kWaitLock.Unlock();
	pSharedData->m_kStartUnlock.Lock();

	pPlatformPos=pSharedData->m_pPlatformSync;
	while (pPlatformPos<pPlatformEnd)
	{
		NjPlatformSyncObject::Unlock(*pPlatformPos);
		++pPlatformPos;
	}

	pImplPos=pSharedData->m_pImplSync;
	while (pImplPos<pImplEnd)
	{
		Static::GetSubSyncObjectTryLockFail(*pImplPos).Unlock();
		++pImplPos;
	}
}
//----------------------------------------------------------------------------
template <>
inline void NjSharedSyncObjectTest<NjPulseEvent>::Static::SharedData02::ThreadFunct(void * pData)
{
	NJ_UNUSED_VAR(pData);
}
//----------------------------------------------------------------------------
template class NjSharedSyncObjectTest<NjDummySyncObject>;
template class NjSharedSyncObjectTest<NjMutex>;
template class NjSharedSyncObjectTest<NjRecursiveMutex>;
template class NjSharedSyncObjectTest<NjSemaphore>;
template class NjSharedSyncObjectTest<NjRWLock, NjRWLock::Read>;
template class NjSharedSyncObjectTest<NjRWLock, NjRWLock::Write>;
template class NjSharedSyncObjectTest<NjPulseEvent>;
//----------------------------------------------------------------------------
